/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Canadian Weather.
 *
 * The Initial Developer of the Original Code is
 *   George Bradt.
 *
 * Portions created by the Initial Developer are Copyright (C) 2011
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   George Bradt.
 *
 * Credits:
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

function parse(aXml, aOptions) {
    var notAvailable = 'N/A';
    var degreeSymbol = "\u00B0";

    var data = {barDisplayString: '',
                location: {name: notAvailable,
                           region: notAvailable,
                           station: notAvailable},
                currentConditions: {date: notAvailable,
                                    condition: notAvailable,
                                    iconCode: '99',             // 99 = flag (not available)
                                    temperature: notAvailable,
                                    dewpoint: notAvailable,
                                    windChill: notAvailable,
                                    humidex: notAvailable,
                                    pressure: notAvailable,
                                    visibility: notAvailable,
                                    relativeHumidity: notAvailable,
                                    wind: notAvailable},
                forecasts: {date: notAvailable,
                            forecast: []},  // [ [period, summary], [period, summary]... ]
                warnings: []};              // [ url, {event}, {event}... ]

    // Root node <siteData>.
    var root = aXml.rootElement;

    if ( !root ) {
        return;
    }

    // Location node.
    var locationNode = root.childElement('location');

    if ( !locationNode ) {
        return;
    }

    // Current conditions node.
    var currentConditionsNode = root.childElement('currentConditions');

    if ( !currentConditionsNode ) {
        return;
    }
    else {
        // Always pick up icon code for current conditions.
        try {
            var iconCodeNode = currentConditionsNode.childElement('iconCode');
            var iconCode = iconCodeNode.text.trim();
            data.currentConditions.iconCode = ( iconCode != '' ) ? iconCode : '99';
        }
        catch(e) {
            data.currentConditions.iconCode = '99';
        }
    }

    // Location name.
    try {
        data.location.name = locationNode.childElement('name').text.trim() || notAvailable;
    }
    catch(e) {
        data.location.name = notAvailable;
    }

    // Observed date.
    try {
        // First dateTime node is UTC, second is local.
        var dateTimeNode = currentConditionsNode.childElementsNamed('dateTime')[1];
        data.currentConditions.date = getDateTimeString(dateTimeNode, aOptions);
    }
    catch(e) {
        data.currentConditions.date = notAvailable;
    }

    if ( aOptions.conditionsPanelEnabled ) {
        // Get all data required for the conditions panel, but not used in bar display.

        // Region.
        try {
            data.location.region = locationNode.childElement('region').text.trim() || notAvailable;
        }
        catch(e) {
            data.location.region = notAvailable;
        }

        // Observation station.
        try {
            data.location.station = currentConditionsNode.childElement('station').text.trim() || notAvailable;
        }
        catch(e) {
            data.location.station = notAvailable;
        }

        // Dewpoint.
        try {
            var dewpointNode = currentConditionsNode.childElement('dewpoint');
            var dewpointValue = dewpointNode.text.trim();
            var dewpointUnits = dewpointNode.attribute('units');
            data.currentConditions.dewpoint = ( dewpointValue != '' ) ? dewpointValue + degreeSymbol + dewpointUnits : notAvailable;
        }
        catch(e) {
            data.currentConditions.dewpoint = notAvailable;
        }

        // Pressure.
        try {
            var pressureNode = currentConditionsNode.childElement('pressure');
            var pressureValue = pressureNode.text.trim();
            var pressureUnits = pressureNode.attribute('units');
            var pressureTendency = pressureNode.attribute('tendency');
            data.currentConditions.pressure = ( pressureValue != '' ) ? pressureValue + ' ' + pressureUnits + ' ' + pressureTendency : notAvailable;
        }
        catch(e) {
            data.currentConditions.pressure = notAvailable;
        }

        // Visibility.
        try {
            var visibilityNode = currentConditionsNode.childElement('visibility');
            var visibilityValue = visibilityNode.text.trim();
            var visibilityUnits = visibilityNode.attribute('units');
            data.currentConditions.visibility = ( visibilityValue != '' ) ? visibilityValue + ' ' + visibilityUnits : notAvailable;
        }
        catch(e) {
            data.currentConditions.visibility = notAvailable;
        }

        // Relative Humidity.
        try {
            var relativeHumidityNode = currentConditionsNode.childElement('relativeHumidity');
            var relativeHumidityValue = relativeHumidityNode.text.trim();
            var relativeHumidityUnits = relativeHumidityNode.attribute('units');
            data.currentConditions.relativeHumidity = ( relativeHumidityValue != '' ) ? relativeHumidityValue + ' ' + relativeHumidityUnits : notAvailable;
        }
        catch(e) {
            data.currentConditions.relativeHumidity = notAvailable;
        }

        // Wind. Multiple nodes, don't use try-catch.
        var windNode = currentConditionsNode.childElement('wind');

        if ( windNode ) {
            var windSpeedNode = windNode.childElement('speed');
            var windGustNode = windNode.childElement('gust');
            var windDirectionNode = windNode.childElement('direction');
            var windSpeedValue, windSpeedUnits, windGustValue, windDirectionValue = '';

            if ( windSpeedNode ) {
                windSpeedValue = windSpeedNode.text.trim();
                var windSpeedUnits = ' ' + windSpeedNode.attribute('units');

                if ( windGustNode ) {
                    var windGustValue = windGustNode.text.trim();
                    windGustValue = ( windGustValue != '') ? ' gust ' + windGustValue : '';
                }

                if ( windDirectionNode ) {
                    var windDirectionValue = windDirectionNode.text.trim() + ' ';
                }

                if ( windSpeedValue ) {
                    if ( windSpeedValue != '0' ) {
                        data.currentConditions.wind = windDirectionValue + windSpeedValue + windGustValue + windSpeedUnits;
                    }
                    else {
                        data.currentConditions.wind = 'Calm';
                    }
                }
            }
        }

        // Forecasts.
        var forecastGroupNode = root.childElement('forecastGroup');

        if ( forecastGroupNode ) {
            // Issued date.
            try {
                // First dateTime node is UTC, second is local.
                var dateTimeNode = forecastGroupNode.childElementsNamed('dateTime')[1];
                data.forecasts.date = getDateTimeString(dateTimeNode, aOptions);
            }
            catch(e) {
                data.forecasts.date = notAvailable;
            }

            // Get all forecasts.
            try {
                var forecastNodes = forecastGroupNode.childElementsNamed('forecast');
                var period, summary = '';

                for ( let i = 0; i < forecastNodes.length; i++ ) {
                    period = forecastNodes[i].childElement('period').text.trim();
                    summary = forecastNodes[i].childElement('textSummary').text.trim();
                    data.forecasts.forecast.push([period, summary]);
                }
            }
            catch(e) {
            }
        }
    }

    // Get data that could appear in the conditions panel, bar display or both.
    // Temperature.
    try {
        var temperatureNode = currentConditionsNode.childElement('temperature');
        var temperatureValue = temperatureNode.text.trim();
        var temperatureUnits = temperatureNode.attribute('units');

        if ( aOptions.toolbarDisplay.showTemperature ) {
            var temperatureString = ( temperatureValue != '' ) ? Math.round(temperatureValue) + degreeSymbol + temperatureUnits : '';
            data.barDisplayString = temperatureString;
        }

        if ( aOptions.conditionsPanelEnabled ) {
            temperatureString = ( temperatureValue != '' ) ? temperatureValue + degreeSymbol + temperatureUnits : notAvailable;
            data.currentConditions.temperature = temperatureString;
        }
    }
    catch(e) {
        data.currentConditions.temperature = notAvailable;
    }

    // Wind chill.
    try {
        var windChill = currentConditionsNode.childElement('windChill').text.trim();

        if ( aOptions.toolbarDisplay.showWindChill ) {
            data.barDisplayString += ( windChill != '' ) ? ' (' + windChill + degreeSymbol + ')' : '';
        }

        if ( aOptions.conditionsPanelEnabled ) {
            data.currentConditions.windChill = ( windChill != '' ) ? windChill + degreeSymbol : notAvailable;
        }
    }
    catch(e) {
        data.currentConditions.windChill = notAvailable;
    }

    // Humidex.
    try {
        var humidex = currentConditionsNode.childElement('humidex').text.trim();

        if ( aOptions.toolbarDisplay.showHumidex ) {
            data.barDisplayString += ( humidex != '' ) ? ' (' + humidex + degreeSymbol + ')' : '';
        }

        if ( aOptions.conditionsPanelEnabled ) {
            data.currentConditions.humidex = ( humidex != '' ) ? humidex + degreeSymbol : notAvailable;
        }
    }
    catch(e) {
        data.currentConditions.humidex = notAvailable;
    }

    // Condition.
    try {
        var condition = currentConditionsNode.childElement('condition').text.trim();

        if ( aOptions.toolbarDisplay.showCondition ) {
            data.barDisplayString += ( condition != '' ) ? ' ' + condition : '';
        }

        if ( aOptions.conditionsPanelEnabled ) {
            data.currentConditions.condition = ( condition != '' ) ? condition : notAvailable;
        }
    }
    catch(e) {
        data.currentConditions.condition = notAvailable;
    }

    if ( aOptions.toolbarDisplayEnabled && data.barDisplayString == '' ) {
        // Make sure there is something to display on bar.
        data.barDisplayString = notAvailable;
    }

    // Get warnings.
    var warningsNode = root.childElement('warnings');

    if ( warningsNode ) {
        var eventNodes = warningsNode.childElementsNamed('event');

        if ( eventNodes.length > 0 ) {
            // Warnings are available.
            var url = warningsNode.attribute('url').trim();
            data.warnings.push(url);

            var date, dateTimeNode, type, priority, description, event;

            for ( let i = 0; i < eventNodes.length; i++ ) {
                type = eventNodes[i].attribute('type').trim();
                type = type.charAt(0).toUpperCase() + type.slice(1) || notAvailable;
                priority = eventNodes[i].attribute('priority').trim();
                priority = priority.charAt(0).toUpperCase() + priority.slice(1) || notAvailable;
                description = eventNodes[i].attribute('description').trim() || notAvailable;

                try {
                    // First dateTime node is UTC, second is local.
                    dateTimeNode = eventNodes[i].childElementsNamed('dateTime')[1];
                    date = getDateTimeString(dateTimeNode, aOptions);
                }
                catch(e) {
                    date = notAvailable;
                }

                event = {type: type,
                         priority: priority,
                         description: description,
                         date: date,
                         location: data.location.name};

                data.warnings.push(event);
            }
        }
    }

	return data;
}

function getDateTimeString(aDateTimeNode, aOptions) {
    try {
        if ( aOptions.language == 'f' ) {
            // Leave French time format as-is.
            var date = aDateTimeNode.childElement('textSummary').text.trim();
        }
        else {
            // English. Unfortunately textSummary uses a 24-hour clock, convert to 12-hour.
            var hour = parseInt(aDateTimeNode.childElement('hour').text.trim(), 10);
            var minute = aDateTimeNode.childElement('minute').text.trim();
            var day = aDateTimeNode.childElement('day').text.trim();
            var month = aDateTimeNode.childElement('month').attribute('name');
            var timezone = aDateTimeNode.attribute('zone');

            var hour12 = hour % 12 || 12;

            if ( hour > 11 ) {
                var ampm = 'PM';
            }
            else {
                ampm = 'AM';
            }

            date = hour12 + ':' + minute + ' ' + ampm + ' ' + timezone + ' on ' + month + ' ' + day;
        }

        return date;
    }
    catch(e) {
        return notAvailable;
    }
}

function dumpNode(aNode) {
    console.debug('*** Dumping node ' + aNode.name);

    for ( var i in aNode ) {
        if ( i == 'childElements' ) {
            if ( aNode[i] ) {
                for ( var j = 0; j < aNode[i].length; j++ ) {
                    console.debug(i + ' ' + aNode[i][j].name);
                }
            }
        }
        else if ( i == 'parentElement' ) {
            console.debug(i + ' ' + aNode[i].name);
        }
        else {
            console.debug(i + ' ' + aNode[i]);
        }

        if ( i == 'text' ) {
            console.debug('*** Completed!');
            return;
        }
    }
}


// Export the 'parse' function.
exports.parse = parse;
